<?php

/*
 *   Original source code : 
 *  (c) 2008 Radim BADSI <info AT devbay DOT fr>
 * 
 */


/* Definition des messages */

class TeleInfo extends CComponent {

    private $trame_def = array(
	  "ADCO" => array("size" => 12, "unit" => "", "desc" => "numéro série compteur"),
	  "OPTARIF" => array("size" => 4, "unit" => "", "desc" => "type d'abonnement"),
	  "ISOUSC" => array("size" => 2, "unit" => "A", "desc" => "intensité souscrite"),
	  "BASE" => array("size" => 9, "unit" => "Wh", "desc" => "index (base)"),
	  "HCHC" => array("size" => 9, "unit" => "Wh", "desc" => "index (heures creuses)"),
	  "HCHP" => array("size" => 9, "unit" => "Wh", "desc" => "index (heures pleines)"),
	  "PTEC" => array("size" => 4, "unit" => "", "desc" => "période tarifaire en cours"),
	  "IINST" => array("size" => 3, "unit" => "A", "desc" => "intensité instantanée"),
	  "ADPS" => array("size" => 3, "unit" => "", "desc" => "avertissement de dépassement de la puissance souscrite"),
	  "IMAX" => array("size" => 3, "unit" => "A", "desc" => "intensité maximale"),
	  "PAPP" => array("size" => 5, "unit" => "W", "desc" => "puissance apparente"),
	  "HHPHC" => array("size" => 1, "unit" => "", "desc" => "groupe horaire (Tempo/HC)"),
	  "MOTDETAT" => array("size" => 6, "unit" => "", "desc" => "mot d'état"),
	  // EJP
	  "EJPHN" => array("size" => 9, "unit" => "Wh", "desc" => "index (normal EJP)"),
	  "EJPHPM" => array("size" => 9, "unit" => "Wh", "desc" => "index (pointe mobile EJP)"),
	  "PEJP" => array("size" => 2, "unit" => "", "	desc" => "préavis période EJP"),
	  // Tempo
	  "BBRHCJB" => array("size" => 9, "unit" => "Wh", "desc" => "index (heures creuses jours bleus Tempo)"),
	  "BBRHPJB" => array("size" => 9, "unit" => "Wh", "desc" => "index (heures pleines jours bleus Tempo)"),
	  "BBRHCJW" => array("size" => 9, "unit" => "Wh", "desc" => "index (heures creuses jours blancs Tempo)"),
	  "BBRHPJW" => array("size" => 9, "unit" => "Wh", "desc" => "index (heures pleines jours blancs Tempo)"),
	  "BBRHCJR" => array("size" => 9, "unit" => "Wh", "desc" => "index (heures creuses jours rouges Tempo)"),
	  "BBRHPJR" => array("size" => 9, "unit" => "Wh", "desc" => "index (heures pleines jours rouges Tempo)"),
	  "DEMAIN" => array("size" => 1, "unit" => "", "desc" => "couleur du lendemain (Tempo)"),
    );
    private $port = "/dev/ttyUSB0";
    private $debug = false;

    function __construct($port = "/dev/ttyUSB0") {
	  $this->port = $port;
    }

    /* Verifie le checksum d'un message 
     * La "checksum" est calculée sur l'ensemble des caractères allant du début du champ étiquette à la fin du champ 
     * donnée, caractère SP inclus. On fait tout d'abord la somme des codes ASCII de tous ces caractères. Pour éviter 
     * d'introduire des fonctions ASCII (00 à 1F en hexadécimal), on ne conserve que les six bits de poids faible du 
     * résultat obtenu (cette opération se traduit par un ET logique entre la somme précédemment calculée et 03Fh). 
     * Enfin, on ajoute 20 en hexadécimal. Le résultat sera donc toujours un caractère ASCII imprimable (signe, chiffre, 
     * lettre majuscule) allant de 20 à 5F en hexadécimal.
     */

    function checksum($etiquette, $valeur) {
	  $controle = $etiquette . " " . $valeur;

	  $sum = 0;
	  for ($i = 0; $i < strlen($controle); $i++) {
		$sum += ord($controle[$i]);
	  }

	  $sum = ($sum & 0x3f) + 0x20;
	  return (chr($sum));
    }

    /* Verifie la taille des messages */

    function messize($etiquette) {
	  /* Renvoyer la taille d'une valeur valide */
	  return $this->trame_def[$etiquette]["size"];
    }

    /* Ecrire la liste des etiquettes valides */

    function etiqlist() {
	  foreach ($this->trame_def as $etiquette => $definition) {
		echo($etiquette . " => " . $definition["desc"] . "\n");
	  }
    }

    /* Recuperer une trame valide */

    function getTeleinfo() {
	  // Resultat : liste etiquette=>valeur
	  $trame_array = array();

	  // Ouvrir le port en lecture
	  if (!$this->debug) {
		$handle = fopen($this->port, "r");

		$char = "";
		$contents = "";

		// On attend le début d'une nouvelle trame
		while (fread($handle, 1) != chr(2));

		// On lit tous les caractères jusqu'au début de la trame suivante
		while ($char != chr(2)) {
		    $char = fread($handle, 1);
		    if ($char != chr(2))
			  $contents .= $char;
		}


		// Fermer le port
		fclose($handle);
	  }
	  else {
		$contents = file_get_contents('teleinfo.txt');
	  }

	  //file_put_contents("teleinfo.txt", $contents);
	  //echo $contents;
	  // Supprimer les caracteres debut/fin de trame
	  $trame = substr($contents, 1, -1);

	  // Separer les messages
	  $trame = str_replace(chr(13), "", $trame);  // Suppression des line feed
	  $trame = explode(chr(10), $trame);   // Découpage selon les retours chariot
	  // Verifier les checksum et supprimer les trames incorrectes
	  foreach ($trame as $key => $message) {

		$valide = true;

		// Separer l'etiquette, la valeur et le checksum
		$message = explode(chr(32), $message, 3);
		list ($etiquette, $valeur, $checksum) = $message;

		// Invalide si incomplet ou incorrect (checksum invalide)
		if (count($message) < 3 ||
			$this->checksum($etiquette, $valeur) != $checksum ||
			$this->messize($etiquette) != strlen($valeur)
		) {
		    $valide = false;
		}

		// Conversion des valeurs numériques
		if ($this->trame_def[$etiquette]["unit"] != "") {
		    $valeur = (int) $valeur;
		}

		// Suppression des "." finaux
		$valeur = trim($valeur, ".");

		$trame_array[$etiquette] = array($valeur, $valide);
	  }

	  return $trame_array;
    }

}
